#!/usr/bin/env python

# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Street Identity Demo Attribute Provider - OAuth 2.0 Client."""

from google.appengine.dist import use_library
use_library('django', '1.2')
from google.appengine.ext import webapp
import logging
from django.utils import simplejson as json

from lib import http
import model
from lib.error import handle_error
import app_params as application_params
from lib.loggable import loggable
from lib import utils

__author__ = "Maciej Machulak"
__maintainer__ = "Maciej Machulak"
__email__ = "mmachulak@google.com"

__copyright__ = "Copyright 2011, Google Inc."
__license__ = "Apache License 2.0"
__version__ = "0.1"
__status__ = "Prototype"

class RedirectHandler(webapp.RequestHandler):

    @loggable('oauth redirect')   
    def get(self):
           
      error = self.request.get('error')
      if error:
        logging.error('error received: %s ' % error)
        handle_error(self)
        return
      
      # Get code from the response
      code = self.request.get('code')
      if code is None or len(code) == 0:
        logging.error('code not present in the request')
        handle_error(self)
        return
      
      logging.debug('received code: %s' % code)

      # Exchange code for access token
      url = application_params.Config.idp
      path = application_params.Config.token_path
      headers = { http.Header.content_type: http.ContentType.urlencoded }
      params = {
          'client_id': application_params.Config.client_id,
          'client_secret': application_params.Config.client_secret,
          'code': code,
          'grant_type': 'authorization_code',
          'redirect_uri': application_params.Config.redirect_uri
      }
      response = http.post_request(url, path, headers, params)
      
      # Check token response from the server
      if response.status != 200:
        logging.error('invalid token response from the server')
        handle_error(self)
        return
      
      logging.debug('correct response status code from the server: %s' % response.status)
      
      response_read = response.read()
      logging.debug('response %s ' % response_read)
      parsed_response = json.loads(response_read)
      logging.debug('access token response: %s ' % parsed_response)
      
      # Get access token and refresh token from the response
      access_token = parsed_response['access_token']
      refresh_token = parsed_response['refresh_token']
        
      # Access user information from user info endpoint
      headers = { http.Header.authorization: http.Header.bearer + ' ' + access_token }
      params = {}
      user_info_path = application_params.Config.user_info_path
      response = http.get_request(url, user_info_path, headers, params)

      if response.status != 200:
        logging.error('invalid user info response from the server')
        handle_error(self)
        return    
      
      logging.debug('correct response status code from the server: %s' % response.status)  
     
      response_read = response.read()
      logging.debug('response %s ' % response_read)
      parsed_response = json.loads(response_read)
      logging.debug('user info response: %s ' % parsed_response)
      
      # Create user locally or retrieve existing user
      user_id = parsed_response['domain'] + parsed_response['user_id']
      user = model.User.all().filter('username = ', user_id).get()
      if user is None:
        logging.debug('creating new user')
        
        # Store user within DB
        user = model.User(
                username = user_id,
                display_name = parsed_response['display_name'],
                email = parsed_response['email'])
        user.put()
        logging.debug('user created successfully')
               
        logging.debug('creating session for the user')
        # Create session for the user
        session = model.Session(
                id = utils.random(),
                user = user,
                access_token = access_token,
                refresh_token = refresh_token)
        session.put()
        logging.debug('session stored successfully for the user')
        
        # Set session cookie
        logging.debug('setting session cookie for the user: %s' % session.id)
        self.response.headers.add_header(http.Header.set_cookie, application_params.Http.SESSION_ID + '=' + str(session.id) + '; Path=/')
        
        # Check state and if we need to redirect the user
        state_id = self.request.get('state')
        if state_id:
          logging.debug('found state: %s ' % state_id)
          state = model.State.all().filter("id = ", state_id).get()
          if state:
            logging.debug('state retrieved from db successfully')
            continue_to = state.continue_to
            logging.debug('continue to address associated with address: %s' % continue_to)
            state.delete()
            logging.debug('deleted state from db')
            logging.debug('redirecting user to: %s ' % continue_to)
            self.redirect(continue_to)
            return
        self.redirect(application_params.Config.SUCCESS_LOGIN_REDIRECT)    
        return       
        
      else:

        # Create session for the existing user
        logging.debug('creating session for the existing user')
        session = model.Session(
                id = utils.random(),
                user = user,
                access_token = access_token,
                refresh_token = refresh_token)
        session.put()
        logging.debug('session created and stored in db')
        
        # Set session cookie
        logging.debug('setting session cookie for the user: %s' % session.id)
        self.response.headers.add_header(http.Header.set_cookie, application_params.Http.SESSION_ID + '=' + str(session.id) + '; Path=/')         
        self.redirect(application_params.Config.SUCCESS_LOGIN_REDIRECT)          
        return   
        

        